package com.nth.android;

import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;

/**
 * Title: Util <br />
 * Copyright: Copyright (c) 2009 <br />
 * NTH Company: NTH d.o.o. <br />
 * 
 * @author Tomica Bosnjak
 * @version 1.0.0
 * @since 1.0.0
 */
public class Util {

    public static final int HTTP_CONNECT_TIMEOUT = 10000;
    public static final int HTTP_READ_TIMEOUT = 10000;

    /**
     * Creates a drawable from URL path.
     * 
     * @param address
     *            to the file
     * @return Drawable object downloaded from remote location.
     */
    public static Drawable downloadDrawable(String address) {
	InputStream is = null;
	Drawable d = null;
	try {
	    URL url = new URL(address);
	    is = (InputStream) url.getContent();
	    d = Drawable.createFromStream(is, "src");
	} catch (MalformedURLException e) {
	    Log.e(Util.class.getName(), e.getMessage(), e);
	} catch (IOException e) {
	    Log.e(Util.class.getName(), e.getMessage(), e);
	} finally {
	    closeStream(is);
	}
	return d;
    }

    /**
     * Downloads bitmap from given url with default settings for connection
     * timeout
     * 
     * @param fileUrl
     * @return Bitmap
     */
    public static Bitmap downloadBitmap(String fileUrl) {
	return Util.downloadBitmap(fileUrl, Util.HTTP_CONNECT_TIMEOUT, Util.HTTP_READ_TIMEOUT);
    }

    /**
     * Downloads bitmap from given url with specified values for connection
     * timeouts
     * 
     * @param fileUrl
     * @param connTimeout
     * @param readTimeout
     * @return Bitmap
     */

    public static Bitmap downloadBitmap(String fileUrl, int connTimeout, int readTimeout) {
	HttpURLConnection connection = null;
	InputStream is = null;

	try {
	    URL imageUrl = new URL(fileUrl);
	    connection = (HttpURLConnection) imageUrl.openConnection();
	    connection.setConnectTimeout(connTimeout);
	    connection.setReadTimeout(readTimeout);
	    connection.setDoInput(true);
	    connection.connect();

	    is = connection.getInputStream();

	    return BitmapFactory.decodeStream(is);
	} catch (IOException ex) {
	    Log.e(Util.class.getName(), ex.getMessage(), ex);
	} finally {
	    if (is != null) {
		try {
		    is.close();
		} catch (IOException ex) {
		    // DO nothing
		}
	    }
	    if (connection != null) {
		connection.disconnect();
	    }
	}
	return null;
    }

    /**
     * Downloads any file to specified location
     * 
     * @param fileUrl
     * @param localFileName
     * @param destinationDir
     * @return boolean
     */
    public static boolean downloadFile(String fileUrl, String localFileName, String destinationDir) {
	URLConnection urlConnection = null;
	InputStream is = null;
	OutputStream os = null;
	try {
	    URL url = new URL(fileUrl);
	    os = new BufferedOutputStream(new FileOutputStream(new File(destinationDir + localFileName)));

	    urlConnection = url.openConnection();
	    is = urlConnection.getInputStream();

	    File file = new File(destinationDir + localFileName);
	    if (!file.exists()) {
		file.createNewFile();
	    }
	    os = new BufferedOutputStream(new FileOutputStream(file));
	    int bytesReaded;
	    byte[] buffer = new byte[1024];
	    while ((bytesReaded = is.read(buffer)) != -1) {
		os.write(buffer, 0, bytesReaded);
	    }

	    return true;
	} catch (Exception ex) {
	    Log.e(Util.class.getName(), ex.getMessage(), ex);
	    return false;
	} finally {
	    if (is != null) {
		try {
		    is.close();
		} catch (IOException e) {
		    // DO nothing
		}
	    }
	    if (os != null) {
		try {
		    os.flush();
		    os.close();
		} catch (IOException e) {
		    // DO nothing
		}
	    }
	}
    }

    /**
     * Method for retriving boolean value from provided int and if int = 0
     * return false. Else returns true
     * <p>
     * 
     * @param value
     *            int with boolean value (0 or 1)
     * @return status in {@link boolean} format
     * */
    public static boolean parseBoolean(int value) {
	return value > 0 ? true : false;
    }

    /**
     * Method for retriving boolean value from provided string and if none is
     * found returns false;
     * <p>
     * 
     * @param value
     *            string with boolean value
     * @return status in {@link boolean} format
     * */
    public static boolean parseBoolean(String value) {
	if (TextUtils.isEmpty(value)) {
	    return false;
	}
	return value.equals("true") || value.equals("1");
    }

    /**
     * Method for retriving number in int format from provided string and if
     * none is found returns default
     * <p>
     * 
     * @param s
     *            string with number
     * @param def
     *            default value that is returned if no int number is found in
     *            string
     * @return number in {@link int} format
     * */
    public static int parseInt(String s, int def) {
	try {
	    return Integer.parseInt(s);
	} catch (NumberFormatException nfe) {
	    return def;
	}
    }

    /**
     * Method for retriving number in long format from provided string and if
     * none is found returns default
     * <p>
     * 
     * @param s
     *            string with number
     * @param def
     *            default value that is returned if no long number is found in
     *            string
     * @return number in {@link Long} format
     * */
    public static long parseLong(String s, long def) {
	try {
	    return Long.parseLong(s);
	} catch (NumberFormatException nfe) {
	    return def;
	}
    }

    /**
     * @deprecated use getFileNameFromURL instead
     */
    @Deprecated
    public static String getImageNameFromURL(String url) {
	if (TextUtils.isEmpty(url)) {
	    return null;
	}
	int index = url.lastIndexOf("/") + 1;
	return url.substring(index, url.length());
    }

    /**
     * 
     * Gets file name from supplied url in string format
     * 
     * @param url
     *            web address that contains filenema
     * @return String with filename
     */
    public static String getFileNameFromURL(String url) {
	if (TextUtils.isEmpty(url)) {
	    return null;
	}
	int index = url.lastIndexOf("/") + 1;

	String string = url.substring(index, url.length());
	string = string.replaceAll("\\\\|\\/|:|\\*|\\?|\"|<|>|\\|", "_");

	return string;
    }

    /**
     * 
     * writes from InputStream to OutputStream with default buffer size of 256
     * 
     * @param is
     *            InputStream
     * @param os
     *            OutputStream
     * @return long total bytes written
     * @throws IOException
     */
    public static long writeFromTo(InputStream is, OutputStream os) throws IOException {
	return writeFromTo(is, os, 256);
    }

    /**
     * 
     * writes from InputStream to OutputStream with specified buffer value
     * 
     * @param is
     * @param os
     * @param chunkSize
     * @return long total bytes written
     * @throws IOException
     */

    public static long writeFromTo(InputStream is, OutputStream os, int chunkSize) throws IOException {
	long total = 0;
	int i = 0;
	byte b[] = new byte[chunkSize];
	while ((i = is.read(b)) != -1) {
	    os.write(b, 0, i);
	    total += i;
	}
	return total;
    }

    /**
     * 
     * Check if file exists in specified context
     * 
     * @param context
     * @param file
     * @return boolean true if file exists, false if not
     */

    public static boolean fileExists(Context context, String file) {
	FileInputStream fis = null;
	try {
	    fis = context.openFileInput(file);
	    return true;
	} catch (FileNotFoundException e) {
	    return false;
	} finally {
	    if (fis != null) {
		try {
		    fis.close();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
	}
    }

    /**
     * 
     * Check if device is connected on mobile or wireless network, does not have
     * to be connected to internet, if it's local it will return true as well
     * 
     * @param context
     * @return boolean true if it has connection, false if not
     */

    public static boolean isDeviceConnected(Context context) {

	ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

	NetworkInfo wifiNet = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
	NetworkInfo mobileNet = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

	if (((wifiNet != null) && wifiNet.isConnected()) || ((mobileNet != null) && mobileNet.isConnected())) {
	    Log.i(Util.class.getName(), "Device is connected!");
	    return true;
	}
	Log.i(Util.class.getName(), "Device is not connected!");
	return false;
    }

    /**
     * Method for retrieving number in short format from provided string and if
     * none is found returns default
     * <p>
     * 
     * @param s
     *            string with number
     * @param def
     *            default value that is returned if no short number is found in
     *            string
     * @return number in {@link short} format
     * */
    public static short parseShort(String s, short def) {
	try {
	    return Short.parseShort(s);
	} catch (NumberFormatException nfe) {
	    return def;
	}
    }

    /**
     * Method for retrieving the length of a remote file. Useful for download
     * percentage calculation.
     * 
     * @param address
     * @return the length of file in bytes, -1 if exception occurs
     * @throws IOException
     */
    public static long getFileSize(String address) {
	URL url = null;
	HttpURLConnection conn = null;
	long result = -1;

	try {
	    url = new URL(address);
	    conn = (HttpURLConnection) url.openConnection();
	    conn.connect();
	    result = conn.getContentLength();
	    return result;
	} catch (MalformedURLException e) {
	    Log.e(Util.class.getName(), e.getMessage(), e);
	    return result;
	} catch (IOException e) {
	    Log.e(Util.class.getName(), e.getMessage(), e);
	    return result;
	} finally {
	    if (conn != null)
		conn.disconnect();
	}
    }

    /**
     * Method to check if a file exists.
     * 
     * @param fileName
     * @return true only if the file exist and is not a folder.
     */
    public static boolean fileExists(String fileName) {
	File file = new File(fileName);
	return file.exists() && file.isFile();
    }

    /**
     * Method that checks if there is a folder with a given name in the
     * application sandbox.
     * 
     * @param Context
     *            c
     * @param String
     *            name
     * @return boolean True if folder exists, false otherwise.
     */
    public static boolean folderExists(Context c, String name) {
	File file = new File(c.getFilesDir().getAbsolutePath() + File.separator + name);
	return file.exists() && file.isDirectory();
    }

    public static Bitmap getBitmapFromFilesDir(Context c, String url) throws GeneralSecurityException {
//	File f = new File(c.getFilesDir().getAbsolutePath() + File.separator + hexEncode(url.getBytes()));
	File f = new File(c.getFilesDir().getAbsolutePath() + File.separator + md5hex(url));
	if (f.exists()) {
	    return BitmapFactory.decodeFile(c.getFilesDir().getAbsolutePath() + File.separator + hexEncode(url.getBytes()));
	}
	return null;
    }

    public static Bitmap getBitmapFromExternalStorage(String path, String url) throws GeneralSecurityException {
//	File f = new File(path + File.separator + hexEncode(url.getBytes()));
	File f = new File(path + File.separator + md5hex(url));
	if (f.exists()) {
//	    return BitmapFactory.decodeFile(path + File.separator + hexEncode(url.getBytes()));
	    return BitmapFactory.decodeFile(path + File.separator + md5hex(url));
	}
	return null;
    }

    /**
     * Helper method for proper stream closing.
     * 
     * @param Closeable
     *            input
     */
    public static void closeStream(Closeable input) {
	if (input != null) {
	    try {
		input.close();
	    } catch (IOException e) {
		Log.e(Util.class.getName(), e.getMessage(), e);
	    }
	}
    }

    /**
     * Method for used testing purposes
     * 
     * */
    public static void main(String[] args) {
	String url = "http://test.bosnjak.com:9999/image.jpg";
	int index = url.lastIndexOf("/") + 1;

	System.out.println(url.substring(index, url.length()));
    }

    public static String md5hex(String data) throws GeneralSecurityException {
	return hexEncode(md5(data.getBytes()));
    }

    public static byte[] md5(byte data[]) throws GeneralSecurityException {
	try {
	    MessageDigest digest = MessageDigest.getInstance("md5");
	    digest.reset();
	    digest.update(data, 0, data.length);
	    return digest.digest();
	} catch (NoSuchAlgorithmException e) {
	    throw e;
	}
    }

    public static String hexEncode(byte[] b) {
	if ((b == null) || (b.length == 0)) {
	    return "";
	}
	/*
	 * if(start < 0 || end > b.length) { throw new
	 * ArrayIndexOutOfBoundsException(); }
	 */
	String res = "";
	for (byte element : b) {
	    String hx = Integer.toHexString(element & 255).toUpperCase();
	    if (hx.length() == 1) {
		res += "0";
	    }
	    res += hx;
	}
	return res;
    }

    public static byte[] hexDecode(String data) {
	if (data == null) {
	    return null;
	} else if (data.length() == 0) {
	    return new byte[0];
	} else if (data.length() % 2 != 0) {
	    throw new IllegalArgumentException("Cannot hex decode, " + data.length() + " % 2 != 0!");
	}
	byte b[] = new byte[data.length() / 2];
	for (int i = 0, j = 0; i < b.length; i++) {
	    b[i] = (byte) Integer.parseInt(data.substring(j, j += 2), 16);
	}
	return b;
    }

    public static void fileCreate(String fileName, InputStream is, Context context) throws IOException, GeneralSecurityException {
	OutputStreamWriter osw = null;
	try {
//	    File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + hexEncode(fileName.getBytes()));
	    File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + md5hex(fileName));
	    OutputStream os = new FileOutputStream(file);
	    osw = new OutputStreamWriter(os);
	    Util.writeFromTo(is, os);
	} finally {
	    if (osw != null) {
		try {
		    osw.flush();
		} catch (IOException e) {
		    // DO nothing
		}
		try {
		    osw.close();
		} catch (IOException e) {
		    // DO nothing
		}
	    }
	}
    }

    public static void fileCreate(String fileName, String path) {
	URLConnection urlConnection = null;
	InputStream is = null;
	try {
	    URL url = new URL(fileName);

	    urlConnection = url.openConnection();
	    is = urlConnection.getInputStream();
	    fileCreate(fileName, is, path);

	} catch (Exception ex) {
	    Log.e(Util.class.getName(), ex.getMessage(), ex);
	} finally {
	    if (is != null) {
		try {
		    is.close();
		} catch (IOException e) {
		    // DO nothing
		}
	    }

	}
    }

    public static void fileCreate(String fileName, InputStream is, String path) throws IOException, GeneralSecurityException {
	OutputStreamWriter osw = null;
	try {
//	    File file = new File(path + File.separator + hexEncode(fileName.getBytes()));
	    File file = new File(path + File.separator + md5hex(fileName));
	    OutputStream os = new FileOutputStream(file);
	    osw = new OutputStreamWriter(os);
	    Util.writeFromTo(is, os);
	} finally {
	    if (osw != null) {
		try {
		    osw.flush();
		} catch (IOException e) {
		    // DO nothing
		}
		try {
		    osw.close();
		} catch (IOException e) {
		    // DO nothing
		}
	    }
	}
    }

    public static boolean fileExistsHex(String fileName, Context context) throws GeneralSecurityException {
//	File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + hexEncode(fileName.getBytes()));
	File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + md5hex(fileName));
	return file.exists() && file.isFile();
    }

    public static boolean fileExistsHex(String fileName, String path) throws GeneralSecurityException {
//	File file = new File(path + File.separator + hexEncode(fileName.getBytes()));
	File file = new File(path + File.separator + md5hex(fileName));
	return file.exists() && file.isFile();
    }

    public static File getFileHex(String fileName, Context context) throws GeneralSecurityException {
//	return new File(context.getFilesDir().getAbsolutePath() + File.separator + hexEncode(fileName.getBytes()));
	return new File(context.getFilesDir().getAbsolutePath() + File.separator + md5hex(fileName));
    }

    public static File getFileHex(String fileName, String path) throws GeneralSecurityException {
//	return new File(path + File.separator + hexEncode(fileName.getBytes()));
	return new File(path + File.separator + md5hex(fileName));
    }

    public static InputStream readFile(String fileName, Context context) throws FileNotFoundException, GeneralSecurityException {
	FileInputStream fis = null;
//	File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + hexEncode(fileName.getBytes()));
	File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + md5hex(fileName));
	if (file.exists()) {
	    fis = new FileInputStream(file);
	} else {
	    throw new FileNotFoundException();
	}
	return fis;
    }

    public static InputStream readFile(String fileName, String path) throws FileNotFoundException, GeneralSecurityException {
	FileInputStream fis = null;
//	File file = new File(path + File.separator + hexEncode(fileName.getBytes()));
	File file = new File(path + File.separator + md5hex(fileName));
	if (file.exists()) {
	    fis = new FileInputStream(file);
	} else {
	    throw new FileNotFoundException();
	}
	return fis;
    }
}
